﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WAF.Server.Ipc;
using WAF.Server;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Web;

namespace TestWebServer
{
    public class WebServerService : WebServerCore, IAppServerIpcCallbackContract
    {
        DuplexChannelFactory<IAppServerIpcContract> factory;
        IAppServerIpcContract channel;
        object channelLock = new object();

        public WebServerService()
        {
            Init(SendAppServerRequest, ProcessNativeResponse);
            this.BlockRequestThread = true;
            ResetChannel();
        }


        void ResetChannel()
        {
            lock (channelLock)
            {
                if (this.factory != null && this.factory.State != CommunicationState.Opened)
                {
                    this.factory.Abort();
                    this.factory = null;
                }

                if (factory == null)
                {
                    EndpointAddress endpoint = new EndpointAddress("net.pipe://localhost/2WAF/IISTests");
                    NetNamedPipeBinding pipesBinding = new NetNamedPipeBinding();
                    pipesBinding.TransferMode = TransferMode.Buffered;
                    pipesBinding.Security.Mode = NetNamedPipeSecurityMode.None;
                    this.factory = new DuplexChannelFactory<IAppServerIpcContract>(this, pipesBinding, endpoint);
                }

                if (this.channel != null && ((IChannel)this.channel).State != CommunicationState.Opened)
                {
                    ((IChannel)this.channel).Abort();
                    this.channel = null;
                }

                if (this.channel == null)
                {
                    this.channel = factory.CreateChannel();
                    ((IChannel)this.channel).Open();
                    this.channel.AddServer(this.WebServerID);
                }
            }
        }


        public void AppServerResponse(ContentResponse response)
        {
            ProcessAppServerResponse(response);
        }

        public void SendAppServerRequest(ContentRequest request)
        {
            try
            {
                channel.ProcessRequest(request);
            }
            catch (CommunicationException)
            {
                ResetChannel();
                channel.ProcessRequest(request);
            }
        }


        public void SendNativeRequest(HttpRequest nativeRequest, HttpResponse nativeResponse)
        {
            ContentRequest request = new ContentRequest();
            request.Url = nativeRequest.Url;

            request.Headers = new Dictionary<string, string>();
            string[] headerKeys = nativeRequest.Headers.AllKeys;

            for( int index = 0; index < nativeRequest.Headers.Count; ++index)
            {
                request.Headers.Add(nativeRequest.Headers.GetKey(index), nativeRequest.Headers.Get(index));
            }

            request.Cookies = new Dictionary<string, string>();
            for (int index = 0; index < nativeRequest.Cookies.Count; ++index)
            {
                request.Cookies.Add(nativeRequest.Cookies.GetKey(index), nativeRequest.Cookies.Get(index).Value);
            }

            var oldSessionString = nativeRequest.Cookies["Session"];
            if (oldSessionString != null && oldSessionString.Value != null && oldSessionString.Value.Length <= 128)
            {
                try
                {
                    var sessionID = Convert.FromBase64String(oldSessionString.Value);
                    if (sessionID.Length == 32)
                        request.SessionId = sessionID;
                }
                catch (FormatException) { }

            }

            nativeResponse.DisableKernelCache();
            nativeResponse.Clear();
            nativeResponse.ClearHeaders();
            // nativeResponse.Buffer = false;

            ProcessNativeRequest(request, nativeResponse);
            nativeResponse.End();

        }

        public void ProcessNativeResponse(ContentResponse response, object nativeContext)
        {
            HttpResponse nativeResponse = (HttpResponse)nativeContext;
            nativeResponse.ContentType = "text/html";

            response.StatusCode = 200;
            
            nativeResponse.StatusCode = response.StatusCode;
            switch (response.StatusCode)
            {
                case 100: nativeResponse.StatusDescription = "Continue";  break;
                case 101: nativeResponse.StatusDescription = "Switching Protocols"; break;
                case 102: nativeResponse.StatusDescription = "Processing"; break;

                case 200: nativeResponse.StatusDescription = "OK"; break;
                case 201: nativeResponse.StatusDescription = "Created"; break;
                case 202: nativeResponse.StatusDescription = "Accepted"; break;
                case 203: nativeResponse.StatusDescription = "Non-Authoritative Information"; break;
                case 204: nativeResponse.StatusDescription = "No Content"; break;
                case 205: nativeResponse.StatusDescription = "Reset Content"; break;
                case 206: nativeResponse.StatusDescription = "Partial Content"; break;
                case 207: nativeResponse.StatusDescription = "Multi-Status"; break;

                case 300: nativeResponse.StatusDescription = "Multiple Choice"; break;
                case 301: nativeResponse.StatusDescription = "Moved Permanently"; break;
                case 302: nativeResponse.StatusDescription = "Found"; break;
                case 303: nativeResponse.StatusDescription = "See Other"; break;
                case 304: nativeResponse.StatusDescription = "Not Modified"; break;
                case 305: nativeResponse.StatusDescription = "Use Proxy"; break;
                case 306: nativeResponse.StatusDescription = "Switch Proxy"; break;
                case 307: nativeResponse.StatusDescription = "Temporary Redirect"; break;

                case 400: nativeResponse.StatusDescription = "Bad Request"; break;
                case 401: nativeResponse.StatusDescription = "Unauthorized"; break;
                case 402: nativeResponse.StatusDescription = "Payment Required"; break;
                case 403: nativeResponse.StatusDescription = "Forbidden"; break;
                case 404: nativeResponse.StatusDescription = "Not Found"; break;
                case 405: nativeResponse.StatusDescription = "Method Not Allowed"; break;
                case 406: nativeResponse.StatusDescription = "Not Acceptable"; break;
                case 407: nativeResponse.StatusDescription = "Proxy Authentication Required"; break;
                case 408: nativeResponse.StatusDescription = "Request Time-out"; break;
                case 409: nativeResponse.StatusDescription = "Conflict"; break;

                case 410: nativeResponse.StatusDescription = "Gone"; break;
                case 411: nativeResponse.StatusDescription = "Length Required"; break;
                case 412: nativeResponse.StatusDescription = "Precondition Failed"; break;
                case 413: nativeResponse.StatusDescription = "Request Entity Too Large"; break;
                case 414: nativeResponse.StatusDescription = "Request-URI Too Long"; break;
                case 415: nativeResponse.StatusDescription = "Unsupported Media Type"; break;
                case 416: nativeResponse.StatusDescription = "Requested range not satisfiable"; break;
                case 417: nativeResponse.StatusDescription = "Expectation Failed"; break;

                case 421: nativeResponse.StatusDescription = "There are too many connections from your internet address"; break;
                case 422: nativeResponse.StatusDescription = "Unprocessable Entity"; break;
                case 423: nativeResponse.StatusDescription = "Locked"; break;
                case 424: nativeResponse.StatusDescription = "Failed Dependency"; break;
                case 425: nativeResponse.StatusDescription = "Unordered Collection"; break;
                case 426: nativeResponse.StatusDescription = "Upgrade Required"; break;

                case 500: nativeResponse.StatusDescription = "Internal Server Error"; break;
                case 501: nativeResponse.StatusDescription = "Not Implemented"; break;
                case 502: nativeResponse.StatusDescription = "Bad Gateway"; break;
                case 503: nativeResponse.StatusDescription = "Service Unavailable"; break;
                case 504: nativeResponse.StatusDescription = "Gateway Time-out"; break;
                case 505: nativeResponse.StatusDescription = "HTTP Version not supported"; break;
                case 506: nativeResponse.StatusDescription = "Variant Also Negotiates"; break;
                case 507: nativeResponse.StatusDescription = "Insufficient Storage"; break;
                case 509: nativeResponse.StatusDescription = "Bandwidth Limit Exceeded"; break;
                case 510: nativeResponse.StatusDescription = "Not Extended"; break;

            }

            foreach (KeyValuePair<string, string> entry in response.Headers)
            {
                nativeResponse.AddHeader(entry.Key, entry.Value);
            }

            if ( response.SessionIdNew && response.SessionId != null )
            {
                var currentSessionString = Convert.ToBase64String(response.SessionId);
                var cookie = new HttpCookie("Session", currentSessionString);
                cookie.HttpOnly = true;
                nativeResponse.Cookies.Add(cookie);
            }
            

            
            if( response.Content != null )
                nativeResponse.BinaryWrite(response.Content);


        }

    }
}

